/**
 * @file shotclock.c
 * @brief Basketball shot clock core logic (24/14 seconds) with independent window
 */

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "shotclock.h"
#include "config.h"
#include "language.h"
#include "drawing/drawing_render.h"
#include "drawing/drawing_time_format.h"
/* Edit mode and window interaction */
#include "drag_scale.h"
#include "window.h"
#include "window/window_core.h"
#include "window/window_visual_effects.h"
#include "window/window_multimonitor.h"
#include "color/color_state.h"

typedef struct {
    BOOL running;
    BOOL paused;
    int remaining_ms;
    int primary_reset_seconds;   /* usually 24 */
    int secondary_reset_seconds; /* usually 14 */
    BOOL topmost;                /* shot clock window topmost */
    HWND hwnd;
    LARGE_INTEGER perf_freq;
    LARGE_INTEGER last_counter;
    BOOL qpc_initialized;
    /* Style & edit-mode overrides */
    BOOL use_override_style;
    BOOL inherit_edit_mode;
    BOOL sc_edit_mode;
    char override_color[10];
    char override_font_name[100];
    float override_font_scale;
    /* Timer interval management */
    int timer_interval_ms;
    /* Local drag state when not inheriting */
    BOOL sc_is_dragging;
    POINT sc_last_cursor;
    /* Local edit-mode helpers */
    BOOL sc_clickthrough_default;   /* click-through when not editing */
    BOOL sc_saved_topmost;          /* previous topmost before local edit */
} ShotClockState;

static ShotClockState g_shotclock = {0};

/* forward declarations */
static void ShotClock_CreateWindowIfNeeded(void);
static void ShotClock_UpdateTitle(void);
static void ShotClock_Invalidate(void);
static void ShotClock_GetDisplayText(wchar_t* buffer, size_t bufferSize);

/* === Internal helpers === */
static inline void InitQpcIfNeeded(void) {
    if (!g_shotclock.qpc_initialized) {
        QueryPerformanceFrequency(&g_shotclock.perf_freq);
        g_shotclock.qpc_initialized = TRUE;
    }
}

static inline int SecondsToMs(int s) { return (s <= 0) ? 0 : (s * 1000); }

/* === Public API === */
void ShotClock_InitFromConfig(void) {
    char config_path[MAX_PATH];
    GetConfigPath(config_path, MAX_PATH);
    /* Defaults: 24 and 14 */
    int pri = ReadIniInt(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_PRIMARY_SECONDS", 24, config_path);
    int sec = ReadIniInt(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_SECONDARY_SECONDS", 14, config_path);
    g_shotclock.primary_reset_seconds = (pri > 0) ? pri : 24;
    g_shotclock.secondary_reset_seconds = (sec > 0) ? sec : 14;
    /* Shot clock topmost & click-through default (independent of main UI) */
    g_shotclock.topmost = ReadIniBool(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_TOPMOST", TRUE, config_path);
    g_shotclock.sc_clickthrough_default = ReadIniBool(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_CLICKTHROUGH", FALSE, config_path);
    /* Independent style & edit-mode options */
    g_shotclock.use_override_style = ReadIniBool(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_USE_OVERRIDE_STYLE", FALSE, config_path);
    /* Default to NOT inheriting main UI edit mode */
    g_shotclock.inherit_edit_mode = ReadIniBool(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_INHERIT_EDIT_MODE", FALSE, config_path);
    g_shotclock.sc_edit_mode = FALSE;
    ReadIniString(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_COLOR", DEFAULT_WHITE_COLOR, g_shotclock.override_color, sizeof(g_shotclock.override_color), config_path);
    ReadIniString(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_FONT_INTERNAL_NAME", "", g_shotclock.override_font_name, sizeof(g_shotclock.override_font_name), config_path);
    char scaleBuf[32] = {0};
    ReadIniString(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_FONT_SCALE", "1.00", scaleBuf, sizeof(scaleBuf), config_path);
    g_shotclock.override_font_scale = (float)atof(scaleBuf);
    if (g_shotclock.override_font_scale <= 0.0f) g_shotclock.override_font_scale = 1.0f;
    if (g_shotclock.override_font_scale < MIN_SCALE_FACTOR) g_shotclock.override_font_scale = MIN_SCALE_FACTOR;
    if (g_shotclock.override_font_scale > MAX_SCALE_FACTOR) g_shotclock.override_font_scale = MAX_SCALE_FACTOR;
    g_shotclock.timer_interval_ms = 100;
    g_shotclock.sc_is_dragging = FALSE;
    g_shotclock.sc_saved_topmost = g_shotclock.topmost;
}

HWND ShotClock_GetHwnd(void) { return g_shotclock.hwnd; }

BOOL ShotClock_IsVisible(void) {
    return g_shotclock.hwnd && IsWindowVisible(g_shotclock.hwnd);
}

BOOL ShotClock_IsRunning(void) { return g_shotclock.running; }

void ShotClock_ShowWindow(void) {
    ShotClock_CreateWindowIfNeeded();
    if (!g_shotclock.hwnd) return;
    ShowWindow(g_shotclock.hwnd, SW_SHOW);
    SetForegroundWindow(g_shotclock.hwnd);
}

void ShotClock_HideWindow(void) {
    if (!g_shotclock.hwnd) return;
    ShowWindow(g_shotclock.hwnd, SW_HIDE);
}

void ShotClock_ToggleVisibility(void) {
    if (!ShotClock_IsVisible()) ShotClock_ShowWindow();
    else ShotClock_HideWindow();
}

void ShotClock_ResetPrimary(void) {
    g_shotclock.remaining_ms = SecondsToMs(g_shotclock.primary_reset_seconds);
    g_shotclock.running = FALSE;
    g_shotclock.paused = FALSE;
    ShotClock_UpdateTitle();
    ShotClock_Invalidate();
}

void ShotClock_ResetSecondary(void) {
    g_shotclock.remaining_ms = SecondsToMs(g_shotclock.secondary_reset_seconds);
    g_shotclock.running = FALSE;
    g_shotclock.paused = FALSE;
    ShotClock_UpdateTitle();
    ShotClock_Invalidate();
}

int ShotClock_GetPrimaryResetSeconds(void) {
    return (g_shotclock.primary_reset_seconds > 0) ? g_shotclock.primary_reset_seconds : 24;
}

int ShotClock_GetSecondaryResetSeconds(void) {
    return (g_shotclock.secondary_reset_seconds > 0) ? g_shotclock.secondary_reset_seconds : 14;
}

void ShotClock_Start(void) {
    ShotClock_CreateWindowIfNeeded();
    if (!g_shotclock.hwnd) return;
    /* If already at 0, do not auto-reset; require manual reset */
    if (g_shotclock.remaining_ms <= 0) {
        g_shotclock.running = FALSE;
        g_shotclock.paused = FALSE;
        ShotClock_UpdateTitle();
        ShotClock_Invalidate();
        return;
    }
    InitQpcIfNeeded();
    QueryPerformanceCounter(&g_shotclock.last_counter);
    g_shotclock.running = TRUE;
    g_shotclock.paused = FALSE;
    /* Base interval 100ms; switch to ~60Hz under last 10s */
    g_shotclock.timer_interval_ms = (g_shotclock.remaining_ms <= 10000) ? 16 : 100;
    SetTimer(g_shotclock.hwnd, 20090, g_shotclock.timer_interval_ms, NULL);
}

void ShotClock_Pause(void) {
    if (!g_shotclock.hwnd) return;
    if (!g_shotclock.running) return;
    g_shotclock.paused = TRUE;
    g_shotclock.running = FALSE;
    KillTimer(g_shotclock.hwnd, 20090);
}

void ShotClock_Resume(void) {
    if (!g_shotclock.hwnd) return;
    if (g_shotclock.remaining_ms <= 0) return;
    InitQpcIfNeeded();
    QueryPerformanceCounter(&g_shotclock.last_counter);
    g_shotclock.paused = FALSE;
    g_shotclock.running = TRUE;
    g_shotclock.timer_interval_ms = (g_shotclock.remaining_ms <= 10000) ? 16 : 100;
    SetTimer(g_shotclock.hwnd, 20090, g_shotclock.timer_interval_ms, NULL);
}

/* === Window glue === */
static LRESULT CALLBACK ShotClock_WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);

static void ShotClock_UpdateTitle(void) {
    if (!g_shotclock.hwnd) return;
    int total_ms = g_shotclock.remaining_ms;
    int seconds = total_ms / 1000;
    wchar_t title[64];
    const wchar_t* base = GetLocalizedString(L"进攻计时器", L"Shot Clock");
    const wchar_t* fmt  = GetLocalizedString(L"%ls - %d秒", L"%ls - %ds");
    swprintf(title, 64, fmt, base, seconds);
    SetWindowTextW(g_shotclock.hwnd, title);
}

static void ShotClock_Invalidate(void) {
    if (g_shotclock.hwnd) InvalidateRect(g_shotclock.hwnd, NULL, TRUE);
}

static void ShotClock_CreateWindowIfNeeded(void) {
    if (g_shotclock.hwnd && IsWindow(g_shotclock.hwnd)) return;

    WNDCLASSW wc = {0};
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = ShotClock_WndProc;
    wc.hInstance = GetModuleHandleW(NULL);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszClassName = L"CatimeShotClockWindow";
    RegisterClassW(&wc);

    /* Align style with main countdown overlay: layered + toolwindow (no taskbar) + popup */
    DWORD exStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW; 
    DWORD style = WS_POPUP;
    HWND hwnd = CreateWindowExW(
        exStyle,
        wc.lpszClassName,
        GetLocalizedString(L"进攻计时器", L"Shot Clock"),
        style,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        260,
        180,
        NULL,
        NULL,
        wc.hInstance,
        NULL
    );

    g_shotclock.hwnd = hwnd;
    /* Color-key transparent background (black) for overlay */
    SetLayeredWindowAttributes(hwnd, RGB(0,0,0), 255, LWA_COLORKEY);
    if (g_shotclock.topmost) {
        SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    }
    ShotClock_ResetPrimary();
    ShowWindow(hwnd, SW_SHOWNOACTIVATE);
    UpdateWindow(hwnd);

    /* Restore position if configured */
    char config_path[MAX_PATH];
    GetConfigPath(config_path, MAX_PATH);
    int posX = ReadIniInt(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_POS_X", -1, config_path);
    int posY = ReadIniInt(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_POS_Y", -1, config_path);
    if (posX >= 0 && posY >= 0) {
        SetWindowPos(hwnd, NULL, posX, posY, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

static void ShotClock_OnPaint(HWND hwnd) {
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    /* Render using shot clock specific display formatting */
    wchar_t buf[32];
    ShotClock_GetDisplayText(buf, sizeof(buf) / sizeof(buf[0]));
    BOOL editMode = g_shotclock.inherit_edit_mode ? CLOCK_EDIT_MODE : g_shotclock.sc_edit_mode;
    if (g_shotclock.use_override_style) {
        RenderCustomTextEx(hwnd, &ps, buf,
                           g_shotclock.override_color,
                           g_shotclock.override_font_name,
                           g_shotclock.override_font_scale,
                           editMode);
    } else {
        RenderCustomTextEx(hwnd, &ps, buf, NULL, NULL, 0.0f, editMode);
    }
    EndPaint(hwnd, &ps);
}

static void ShotClock_OnTimer(HWND hwnd) {
    if (!g_shotclock.running || g_shotclock.paused) return;
    LARGE_INTEGER now; QueryPerformanceCounter(&now);
    LONGLONG ticks = now.QuadPart - g_shotclock.last_counter.QuadPart;
    g_shotclock.last_counter = now;
    double elapsed_ms = (double)ticks * 1000.0 / (double)g_shotclock.perf_freq.QuadPart;
    g_shotclock.remaining_ms -= (int)elapsed_ms;
    if (g_shotclock.remaining_ms <= 0) {
        g_shotclock.remaining_ms = 0;
        g_shotclock.running = FALSE;
        KillTimer(hwnd, 20090);
        /* optional: beep */
        MessageBeep(MB_ICONASTERISK);
    }
    /* Adjust timer interval when crossing 10s threshold */
    int desired = (g_shotclock.remaining_ms <= 10000 && g_shotclock.running) ? 16 : 100;
    if (desired != g_shotclock.timer_interval_ms) {
        g_shotclock.timer_interval_ms = desired;
        KillTimer(hwnd, 20090);
        SetTimer(hwnd, 20090, g_shotclock.timer_interval_ms, NULL);
    }
    ShotClock_UpdateTitle();
    ShotClock_Invalidate();
}

static LRESULT CALLBACK ShotClock_WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
        case WM_CREATE:
            /* Default to click-through until edit mode (shot clock specific) */
            SetClickThrough(hwnd, g_shotclock.sc_clickthrough_default);
            return 0;
        case WM_PAINT:
            ShotClock_OnPaint(hwnd);
            return 0;
        case WM_TIMER:
            if (wp == 20090) { ShotClock_OnTimer(hwnd); return 0; }
            break;
        case WM_SETCURSOR:
            if ((g_shotclock.inherit_edit_mode ? CLOCK_EDIT_MODE : g_shotclock.sc_edit_mode) && LOWORD(lp) == HTCLIENT) {
                SetCursor(LoadCursorW(NULL, IDC_ARROW));
                return TRUE;
            }
            if (LOWORD(lp) == HTCLIENT) {
                SetCursor(LoadCursorW(NULL, IDC_ARROW));
                return TRUE;
            }
            break;
        case WM_LBUTTONDOWN:
            if (g_shotclock.inherit_edit_mode) {
                StartDragWindow(hwnd);
            } else if (g_shotclock.sc_edit_mode) {
                g_shotclock.sc_is_dragging = TRUE;
                POINT p; GetCursorPos(&p); g_shotclock.sc_last_cursor = p;
                SetCapture(hwnd);
            }
            return 0;
        case WM_LBUTTONUP:
            if (g_shotclock.inherit_edit_mode) {
                EndDragWindow(hwnd);
            } else if (g_shotclock.sc_is_dragging) {
                g_shotclock.sc_is_dragging = FALSE;
                ReleaseCapture();
                /* Persist position */
                RECT rc; GetWindowRect(hwnd, &rc);
                char config_path[MAX_PATH]; GetConfigPath(config_path, MAX_PATH);
                WriteIniInt(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_POS_X", rc.left, config_path);
                WriteIniInt(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_POS_Y", rc.top, config_path);
            }
            return 0;
        case WM_MOUSEMOVE:
            if (g_shotclock.inherit_edit_mode) {
                if (HandleDragWindow(hwnd)) return 0;
            } else if (g_shotclock.sc_is_dragging) {
                POINT p; GetCursorPos(&p);
                int dx = p.x - g_shotclock.sc_last_cursor.x;
                int dy = p.y - g_shotclock.sc_last_cursor.y;
                g_shotclock.sc_last_cursor = p;
                RECT rc; GetWindowRect(hwnd, &rc);
                int x = rc.left + dx;
                int y = rc.top + dy;
                SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
                return 0;
            }
            break;
        case WM_MOUSEWHEEL: {
            int delta = GET_WHEEL_DELTA_WPARAM(wp);
            if (g_shotclock.inherit_edit_mode) {
                HandleScaleWindow(hwnd, delta);
            } else if (g_shotclock.sc_edit_mode) {
                float factor = (delta > 0) ? 1.1f : (1.0f / 1.1f);
                g_shotclock.override_font_scale *= factor;
                if (g_shotclock.override_font_scale < MIN_SCALE_FACTOR) g_shotclock.override_font_scale = MIN_SCALE_FACTOR;
                if (g_shotclock.override_font_scale > MAX_SCALE_FACTOR) g_shotclock.override_font_scale = MAX_SCALE_FACTOR;
                char config_path[MAX_PATH]; GetConfigPath(config_path, MAX_PATH);
                char buf[32]; _snprintf(buf, sizeof(buf), "%.2f", g_shotclock.override_font_scale);
                WriteIniString(INI_SECTION_SHOTCLOCK, "SHOTCLOCK_FONT_SCALE", buf, config_path);
                InvalidateRect(hwnd, NULL, TRUE);
                return 0;
            }
            return 0;
        }
        case WM_RBUTTONDOWN:
            if (GetKeyState(VK_CONTROL) & 0x8000) {
                if (g_shotclock.inherit_edit_mode) {
                    CLOCK_EDIT_MODE = !CLOCK_EDIT_MODE;
                    SetClickThrough(hwnd, !CLOCK_EDIT_MODE ? TRUE : FALSE);
                } else {
                    g_shotclock.sc_edit_mode = !g_shotclock.sc_edit_mode;
                    if (g_shotclock.sc_edit_mode) {
                        /* Enter local edit mode: enable blur, disable click-through, force topmost */
                        g_shotclock.sc_saved_topmost = g_shotclock.topmost;
                        if (!g_shotclock.topmost) {
                            SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                                         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                            g_shotclock.topmost = TRUE;
                        }
                        SetBlurBehind(hwnd, TRUE);
                        SetClickThrough(hwnd, FALSE);
                        ShowWindow(hwnd, SW_SHOW);
                        SetForegroundWindow(hwnd);
                    } else {
                        /* Exit local edit mode: restore blur, click-through, and topmost */
                        SetBlurBehind(hwnd, FALSE);
                        SetClickThrough(hwnd, g_shotclock.sc_clickthrough_default);
                        if (!g_shotclock.sc_saved_topmost) {
                            SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
                                         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                            g_shotclock.topmost = FALSE;
                        }
                    }
                }
                InvalidateRect(hwnd, NULL, TRUE);
                return 0;
            }
            break;
        case WM_RBUTTONUP:
            if (g_shotclock.inherit_edit_mode && CLOCK_EDIT_MODE) { EndEditMode(hwnd); return 0; }
            break;
        case WM_LBUTTONDBLCLK:
            if (g_shotclock.inherit_edit_mode && !CLOCK_EDIT_MODE) { StartEditMode(hwnd); return 0; }
            break;
        case WM_WINDOWPOSCHANGED:
            if (g_shotclock.inherit_edit_mode && CLOCK_EDIT_MODE) SaveWindowSettings(hwnd);
            break;
        case WM_DISPLAYCHANGE:
            AdjustWindowPosition(hwnd, TRUE);
            InvalidateRect(hwnd, NULL, FALSE);
            UpdateWindow(hwnd);
            return 0;
        case WM_CLOSE:
            ShowWindow(hwnd, SW_HIDE);
            return 0;
        case WM_DESTROY:
            KillTimer(hwnd, 20090);
            g_shotclock.hwnd = NULL;
            return 0;
        default:
            break;
    }
    return DefWindowProcW(hwnd, msg, wp, lp);
}

/* === Formatting helpers === */
static void ShotClock_GetDisplayText(wchar_t* buffer, size_t bufferSize) {
    if (!buffer || bufferSize == 0) return;

    /* Empty string when not initialized */
    if (g_shotclock.remaining_ms < 0) {
        buffer[0] = L'\0';
        return;
    }

    /* When zero, keep "0" to indicate timeout reached (consistent with main logic showing empty only for specific cases) */
    if (g_shotclock.remaining_ms == 0) {
        _snwprintf_s(buffer, bufferSize, _TRUNCATE, L"0");
        return;
    }
    /* Shot clock specific formatting:
       - Show hundredths (两位小数) during last 10 seconds
       - Otherwise show integer seconds, no zero padding */
    int remaining = g_shotclock.remaining_ms;
    if (remaining < 0) remaining = 0;

    if (remaining <= 10000) {
        int seconds = remaining / 1000;                 /* floor seconds */
        int centiseconds = (remaining % 1000) / 10;     /* 0..99 */
        _snwprintf_s(buffer, bufferSize, _TRUNCATE, L"%d.%02d", seconds, centiseconds);
    } else {
        int seconds = remaining / 1000;                 /* floor seconds */
        _snwprintf_s(buffer, bufferSize, _TRUNCATE, L"%d", seconds);
    }
}